package preprocess;

import java.io.File;
import java.util.ArrayList;

import utils.ThreadCritical;

import classification.elemListRepr.XMLElementList;
import classification.slvm.ElementSimilarityMatrix;

import com.db4o.*;
import com.db4o.config.*;
import static utils.Configuration.*;

@SuppressWarnings( { "unchecked", "unused" })
public class DBWrapper implements DBManage
{
    private static final String DB_PATH              = utils.Configuration.DatabasePath;
    private static final String DB_NAME_SUFFIX       = ".yap";

    private ThreadCritical      threadCriticalForSSM = null;
    private ThreadCritical      threadCriticalForXEL = null;

    public DBWrapper()
    {
        this.threadCriticalForSSM = new ThreadCritical();
        this.threadCriticalForXEL = new ThreadCritical();
    }

    /**
     * Create a new database according the DBName
     * 
     * @param DBName
     *            Specify the database name
     * @return true if create succeed, false if there is already a database of
     *         same name exist or any other error occur in create process
     */
    public ObjectContainer createDB(String DBName)
    {
        ObjectContainer database = null;
        try
        {
            Configuration config = Db4o.newConfiguration();
            config.lockDatabaseFile(false);
            config.automaticShutDown(false);

            String DBFullName = DB_PATH + DBName + DB_NAME_SUFFIX;
            File DBExistTest = new File(DBFullName);
            if (DBExistTest.exists())
            {
                System.err.println("Database has already exist");
                return null;
            }
            else
            {
                database = Db4o.openFile(config, DBFullName);
                return database;
            }
        }
        catch (Exception ex)
        {
            return null;
        }
        finally
        {
            if (database != null)
                database.close();
        }
    }

    /**
     * Open database for advanced operation
     * 
     * @param DBName
     *            Specify database to be open
     * @return true if open succeed, false otherwise
     */
    private ObjectContainer openDB(String DBName)
    {
        ObjectContainer database = null;

        try
        {
            Configuration config = Db4o.newConfiguration();
            config.lockDatabaseFile(false);
            config.automaticShutDown(false);

            database = Db4o.openFile(config, DB_PATH + DBName + DB_NAME_SUFFIX);
            return database;
        }
        catch (Exception ex)
        {
            return null;
        }
    }

    /**
     * Insert <b> XML document </b> object into database
     * 
     * @param doc
     *            The XML document object to be inserted
     * @param DBName
     *            DBName indicate the database to be insert
     * @return true if insert succeed, false if there is already a document
     *         object of same name exist or any other error occur in insert
     *         process
     */
    public boolean insert(XMLDoc doc, String DBName)
    {
        ObjectContainer db = this.openDB(DBName);
        try
        {
            XMLDoc xmlDoc = doc;
            ObjectSet result = db.queryByExample(xmlDoc);
            if (!result.isEmpty())
            {
                return false;
            }
            else
            {
                db.store(xmlDoc);
                return true;
            }
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }

    /**
     * Insert <b> XMLElementList </b> object into database
     * 
     * @param xmlElemList
     *            The XMLElementList object to be inserted
     * @param DBName
     *            DBName indicate the database to be insert
     * @return true if insert succeed, false if there is already a
     *         XMLElementList object of same name exist or any other error occur
     *         in insert process
     */
    public boolean insert(XMLElementList xmlElemList, String DBName)
    {
        this.threadCriticalForXEL.getLock();
        ObjectContainer db = this.openDB(DBName);
        try
        {
            XMLElementList xml = xmlElemList;
            ObjectSet result = db.queryByExample(xml);
            if (!result.isEmpty())
            {
                return false;
            }
            else
            {
                db.store(xml);
                return true;
            }
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            if (db != null)
                db.close();
            this.threadCriticalForXEL.releaseLock();
        }
    }

    /**
     * Insert <b> ArrayList(XMLElementList) </b> object into database
     * 
     * @param list
     *            XMLELRList object to be insert
     * @param DBName
     *            DBName indicate the database to be insert
     * @return true if insert succeed, false if there is already a XMLELRList
     *         object of same name exist or any other error occur in insert
     *         process
     */
    public boolean insert(XMLELRList list, String DBName)
    {
        ObjectContainer db = this.openDB(DBName);
        try
        {
            XMLELRList XMLList = list;
            ObjectSet result = db.queryByExample(XMLList);
            if (!result.isEmpty())
            {
                return false;
            }
            else
            {
                db.store(XMLList);
                return true;
            }
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }

    /**
     * Insert <b> ArrayList(XMLElementList) </b> object into database
     * 
     * @param list
     *            ElementSimilarityMatrix object to be insert
     * @param DBName
     *            DBName indicate the database to be insert
     * @return true if insert succeed, false if there is already a
     *         ElementSimilarityMatrix object of same name exist or any other
     *         error occur in insert process
     */
    public boolean insert(ElementSimilarityMatrix simiMatrix, String DBName)
    {
        this.threadCriticalForSSM.getLock();
        ObjectContainer db = this.openDB(DBName);
        try
        {
            ElementSimilarityMatrix matrix = simiMatrix;
            ObjectSet result = db.queryByExample(matrix);
            if (!result.isEmpty())
            {
                return false;
            }
            else
            {
                db.store(matrix);
                return true;
            }
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            if (db != null)
                db.close();
            this.threadCriticalForSSM.releaseLock();
        }
    }

    /**
     * Get the <b> XML document </b> object by specified <b> docID </b>
     * 
     * @param DBName
     *            Specify the database to be queried
     * @param DocID
     *            The XML document ID
     * @return The XML document object according to the DocID
     */
    public XMLDoc queryXMLDoc(String DBName, String docID)
    {
        ObjectContainer db = this.openDB(DBName);
        XMLDoc doc = null;
        try
        {
            ObjectSet result = db.queryByExample(new XMLDoc(docID));

            if (result.isEmpty())
            {
                return null;
            }
            else
            {
                doc = (XMLDoc) result.next();
            }
            return doc;
        }
        catch (Exception ex)
        {
            return null;
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }

    /**
     * Get the <b> XMLElementList </b> object by specified <b> docID </b>
     * 
     * @param DBName
     *            Specify the database to be queried
     * @param listID
     *            The XMLElementList ID
     * @return The XMLElementList object according to the listID
     */
    public XMLElementList queryXMLElemList(String DBName, String listID)
    {
        this.threadCriticalForXEL.getLock();
        ObjectContainer db = this.openDB(DBName);
        XMLElementList xmlElemList = null;
        try
        {
            ObjectSet result = db.queryByExample(new XMLElementList(listID));

            if (result.isEmpty())
            {
                return null;
            }
            else
            {
                xmlElemList = (XMLElementList) result.next();
            }
            return xmlElemList;
        }
        catch (Exception ex)
        {
            return null;
        }
        finally
        {
            if (db != null)
                db.close();
            this.threadCriticalForXEL.releaseLock();
        }
    }

    /**
     * Get the <b> XMLELRList </b> object according to specified listID
     * 
     * @param DBName
     *            Specify the database to be queried
     * @param listID
     *            The XMLELRList ID
     * @return The XMLELRList object according to the listID
     */
    public XMLELRList queryXMLELRList(String DBName, String listID)
    {
        ObjectContainer db = this.openDB(DBName);
        XMLELRList list = null;
        try
        {
            ObjectSet result = db.queryByExample(new XMLELRList(listID));

            if (result.isEmpty())
            {
                return null;
            }
            else
            {
                list = (XMLELRList) result.next();
            }
            return list;
        }
        catch (Exception ex)
        {
            return null;
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }

    /**
     * Get the <b> ElementSimilarityMatrix </b> object according to specified
     * simiMatrixID
     * 
     * @param DBName
     *            Specify the database to be queried
     * @param listID
     *            The ElementSimilarityMatrix ID
     * @return The ElementSimilarityMatrix object according to the simiMatrixID
     */
    public ElementSimilarityMatrix queryElemSimiMatrix(String DBName,
            String simiMatrixID)
    {
        this.threadCriticalForSSM.getLock();
        ObjectContainer db = this.openDB(DBName);
        ElementSimilarityMatrix simiMatrix = null;
        try
        {
            ObjectSet result = db.queryByExample(new ElementSimilarityMatrix(
                    simiMatrixID));

            if (result.isEmpty())
            {
                return null;
            }
            else
            {
                simiMatrix = (ElementSimilarityMatrix) result.next();
            }
            return simiMatrix;
        }
        catch (Exception ex)
        {
            return null;
        }
        finally
        {
            if (db != null)
                db.close();
            this.threadCriticalForSSM.releaseLock();
        }
    }

    /**
     * Reset the DBName specified database to be empty
     * 
     * @param DBName
     *            The database which is specified to be empty
     * @return true if reset succeed, false otherwise
     */
    public boolean resetDB(String DBName)
    {
        ObjectContainer database = null;
        try
        {
            Configuration config = Db4o.newConfiguration();
            config.lockDatabaseFile(false);
            config.automaticShutDown(false);
            new File(DB_PATH + DBName + DB_NAME_SUFFIX).delete();
            database = Db4o.openFile(config, DB_PATH + DBName + DB_NAME_SUFFIX);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    /**
     * Delete the DBName specified database
     * 
     * @param DBName
     *            The database to be deleted
     * @return true if delete succeed, false otherwise
     */
    public boolean deleteDB(String DBName)
    {
        try
        {
            Configuration config = Db4o.newConfiguration();
            config.lockDatabaseFile(false);
            config.automaticShutDown(false);
            new File(DB_PATH + DBName + DB_NAME_SUFFIX).delete();
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    /**
     * Find out whether the database specified by <B>DBName</B> is existed
     * 
     * @param DBName
     *            The database name which to be tested
     * @return true if there is a database of the same name <B>DBName</B> is
     *         exist, false otherwise
     */
    public boolean exists(String DBName)
    {
        String DBFullName = DB_PATH + DBName + DB_NAME_SUFFIX;
        File DBExistTest = new File(DBFullName);
        if (DBExistTest.exists())
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public static void main(String[] args)
    {
        DBWrapper db = new DBWrapper();

        db.createDB("DBWrapperTest");

        db.insert(new XMLDoc(""), "DBWrapperTest");

        db.resetDB("DBWrapperTest");

        XMLDoc doc = db.queryXMLDoc("DBWrapperTest", "a");
    }
}
